#!/usr/bin/perl
# Copyright (C) 2011-2013 Zentyal S.L.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2, as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

# Minimalistic perlconsole clone with Zentyal environment preloaded

use warnings;
use strict;

use Term::ReadLine;
use Lexical::Persistence;
use Data::Dumper;
use TryCatch;

use EBox;
use EBox::Global;
use EBox::Model::Manager;

EBox::init();

my $PROMPT = 'zentyal> ';

my $term = new Term::ReadLine $PROMPT;
my $lex = new Lexical::Persistence;

my $globalInit = code_with_lex_environ('my $global = EBox::Global->getInstance()');
&$globalInit();

my $internal;
my @commands = parse_commandline();

while (defined (my $line = get_command())) {
    last if ($line eq 'exit');
    $internal = 1;
    $line = parse_internal_commands($line);
    next unless $line;
    my $code = code_with_lex_environ($line);
    next unless defined $code;
    eval_and_print($code);
}

sub parse_commandline
{
    my @cmds = map { split (';', $_) } @ARGV;
    push (@cmds, 'exit') if @cmds;
    return @cmds;
}

sub get_command
{
    if (@commands) {
	return shift (@commands);
    } else {
        return $term->readline($PROMPT);
    }
}

sub eval_and_print
{
    my ($code) = @_;

    my $ret = eval { &$code(); };
    print $@ if $@;
    if ((ref ($ret) eq 'ARRAY') or (ref ($ret) eq 'HASH')) {
        print Dumper($ret);
    } else {
        print "$ret\n" if ($ret and not $internal);
    }
}

sub parse_internal_commands
{
    my ($cmd) = @_;

    my ($mod) = $cmd =~ /^instance (\w+);*/;
    if ($mod) {
        unless (EBox::Global->modExists($mod)) {
            print "Error: Module '$mod' not found\n";
            return undef;
        }
        return "my \$$mod = \$global->modInstance('$mod'); print '\$$mod\n';";
    }
    my ($model) = $cmd =~ /^model (\w+);*/;
    if ($model) {
        my $var = lc($model);
        return "my \$$var; try { \$$var = EBox::Model::Manager->instance()->model('$model'); print '\$$var\n'; } catch { print \"Error: Model '$model' not found\n\" };";
    }
    my ($call) = $cmd =~ /^call (.+);*/;
    if ($call) {
        my @parts = split ('::', $call);
        if (@parts) {
            @parts = map { split ('->', $_) } @parts;
            pop (@parts);
            $call = 'use ' . join ('::', @parts) . "; $call;";
        }
        return $call;
    }

    $internal = 0;
    return $cmd;
}

sub code_with_lex_environ
{
    my ($code) = @_;

    my $vars = '';
    foreach my $var (keys %{$lex->get_context('_')}) {
        $vars .= "my $var;";
    }
    my $sub = "sub {$vars $code}";
    my $ref = eval $sub;
    if ($@) {
        print $@;
        return undef;
    }
    return $lex->wrap($ref);
}
